/*
 * Copyright 2008 Wink Saville
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.saville.msgcomp;

import java.util.concurrent.atomic.*;

import com.saville.serdes.*;

public class Msg implements ISerdes<Msg> {
    public static final int SERDES_ID = 2;
    public static final String SERDES_ID_STRING = "2";

    transient public IMc handler;

    public boolean remote = false;
    public int dstId = -1;
    public int dstSubId;
    public int srcId = -1;
    public int srcSubId;
    public int guid;
    public int cmd;
    public int tag;
    public int timeout;
    public int status;
    public long arg1;
    public long arg2;
    public long arg3;
    public long arg4;

    public Object obj;

    public boolean is(int guid, int cmd) {
        return (this.guid == guid) && (this.cmd == cmd);
    }

    public boolean cmdIsRequest() {
        return (McConst.cmdIsRequest(this.cmd));
    }

    public boolean cmdIsNotRequest() {
        return !cmdIsRequest();
    }

    public boolean cmdIsReply(int guid, int cmd) {
        return is(guid, McConst.cmdToReply(cmd));
    }

    public boolean cmdIsReply() {
        return (McConst.cmdIsReply(this.cmd));
    }

    public boolean cmdIsNotReply() {
        return !cmdIsReply();
    }

    public boolean isStatusOk() {
        return status == McConst.STATUS_OK;
    }
    
    public String serdesIdString() {
        return SERDES_ID_STRING;
    }

    @Override
    public int serdesId() {
        return SERDES_ID;
    }

    /* TODO: Should this come from a pool? This is simpler. */
    @Override
    public ISerdes<Msg> make() {
        return new Msg();
    }

    @Override
    public void serialize(SerdesBinary serdes) {
        serdes.writeInt(serdesId());
        serdes.writeBoolean(remote);
        serdes.writeInt(dstId);
        serdes.writeInt(dstSubId);
        serdes.writeInt(srcId);
        serdes.writeInt(srcSubId);
        serdes.writeInt(guid);
        serdes.writeInt(cmd);
        serdes.writeInt(tag);
        serdes.writeInt(timeout);
        serdes.writeInt(status);
        serdes.writeLong(arg1);
        serdes.writeLong(arg2);
        serdes.writeLong(arg3);
        serdes.writeLong(arg4);

        serdes.writeObject(obj);
    }

    @Override
    public Msg deserialize(SerdesBinary serdes) {

        remote = serdes.readBoolean();
        dstId = serdes.readInt();
        dstSubId = serdes.readInt();
        srcId = serdes.readInt();
        srcSubId = serdes.readInt();
        guid = serdes.readInt();
        cmd = serdes.readInt();
        tag = serdes.readInt();
        timeout = serdes.readInt();
        status = serdes.readInt();
        arg1 = serdes.readLong();
        arg2 = serdes.readLong();
        arg3 = serdes.readLong();
        arg4 = serdes.readLong();
        obj = serdes.readObject();

        return this;
    }

    @Override
    public void serialize(SerdesJson serdes) {
        try {
            serdes.startObject();
            serdes.writeKey(serdesIdString());
            serdes.startArray();
            serdes.writeBoolean(remote);
            serdes.writeInt(dstId);
            serdes.writeInt(dstSubId);
            serdes.writeInt(srcId);
            serdes.writeInt(srcSubId);
            serdes.writeInt(guid);
            serdes.writeInt(cmd);
            serdes.writeInt(tag);
            serdes.writeInt(timeout);
            serdes.writeInt(status);
            serdes.writeLong(arg1);
            serdes.writeLong(arg2);
            serdes.writeLong(arg3);
            serdes.writeLong(arg4);
    
            serdes.writeObject(obj);
            serdes.endArray();
            serdes.endObject();
        } catch (Exception e) {
            throw new RuntimeException("Msg#serialize json exception");
        }
    }

    @Override
    public Msg deserialize(SerdesJson serdes) {
        try {
            serdes.startArrayRead();
            remote = serdes.readBoolean();
            dstId = serdes.readInt();
            dstSubId = serdes.readInt();
            srcId = serdes.readInt();
            srcSubId = serdes.readInt();
            guid = serdes.readInt();
            cmd = serdes.readInt();
            tag = serdes.readInt();
            timeout = serdes.readInt();
            status = serdes.readInt();
            arg1 = serdes.readLong();
            arg2 = serdes.readLong();
            arg3 = serdes.readLong();
            arg4 = serdes.readLong();
            obj = serdes.readObject();
            serdes.endArrayRead();
        } catch (Exception e) {
            throw new RuntimeException("StringSer#deserialize json error");
        }
        return this;
    }

    /*
     * This code should be present in all classes that implement ISerdes
     */
    {
        if (registered.compareAndSet(0, 1)) {
            SerdesBinary.register(serdesId(), this);
            SerdesJson.register(serdesId(), this);
        }
    }

    private static final AtomicInteger registered = new AtomicInteger();
}
